package zlib

import (
	"errors"
	"fmt"
	"go/parser"
	"go/token"
	"path/filepath"
	"reflect"
	"runtime"
	"strings"
)

// dumpPrint 打印调试变量,变量可多个.
func DumpPrint(vs ...interface{}) {
	for _, v := range vs {
		fmt.Printf("%+v\n", v)
	}
}

// MethodExists 检查val结构体中是否存在methodName方法.
func MethodExists(val interface{}, methodName string) (bool, error) {
	if methodName == "" {
		return false, errors.New("[methodExists]`methodName can not be empty.")
	}

	r := reflect.ValueOf(val)
	if r.Type().Kind() != reflect.Ptr {
		r = reflect.New(reflect.TypeOf(val))
	}

	method := r.MethodByName(methodName)
	if !method.IsValid() {
		return false, fmt.Errorf("[methodExists] Method `%s` not exists in interface `%s`", methodName, r.Type())
	}

	return true, nil
}

// GetMethod 获取val结构体的methodName方法.
// 注意:返回的方法中的第一个参数是接收者.
// 所以,调用返回的方法时,必须将接收者作为第一个参数传递.
func GetMethod(val interface{}, methodName string) interface{} {
	if val == nil || methodName == "" {
		return nil
	}

	r := reflect.ValueOf(val)
	if r.Type().Kind() != reflect.Ptr {
		r = reflect.New(reflect.TypeOf(val))
	}

	method := r.MethodByName(methodName)
	if !method.IsValid() {
		return nil
	}

	return method.Interface()
}

// GetFuncNames 获取变量的所有函数名.
func GetFuncNames(val interface{}) (res []string) {
	if val == nil {
		return
	}

	r := reflect.ValueOf(val)
	if r.Type().Kind() != reflect.Ptr {
		r = reflect.New(reflect.TypeOf(val))
	}

	typ := r.Type()
	for i := 0; i < r.NumMethod(); i++ {
		res = append(res, typ.Method(i).Name)
	}
	return
}

// GetVariateType 获取变量类型.
func GetVariateType(v interface{}) string {
	return fmt.Sprintf("%T", v)
}

// GetVariatePointerAddr 获取变量的指针地址.
func GetVariatePointerAddr(val interface{}) int64 {
	var p string
	v := reflect.ValueOf(val)
	switch v.Kind() {
	case reflect.Ptr: //变量是指针
		p = fmt.Sprintf("%p", val)
	default:
		p = fmt.Sprintf("%p", &val)
	}

	res, _ := HexToDec(p)
	return res
}

// VerifyFunc 验证是否函数,并且参数个数、类型是否正确.
// 返回有效的函数、有效的参数.
func VerifyFunc(f interface{}, args ...interface{}) (vf reflect.Value, vargs []reflect.Value, err error) {
	vf = reflect.ValueOf(f)
	if vf.Kind() != reflect.Func {
		return reflect.ValueOf(nil), nil, fmt.Errorf("[VerifyFunc] %v is not the function", f)
	}

	tf := vf.Type()
	_len := len(args)
	if tf.NumIn() != _len {
		return reflect.ValueOf(nil), nil, fmt.Errorf("[VerifyFunc] %d number of the argument is incorrect", _len)
	}

	vargs = make([]reflect.Value, _len)
	for i := 0; i < _len; i++ {
		typ := tf.In(i).Kind()
		if (typ != reflect.Interface) && (typ != reflect.TypeOf(args[i]).Kind()) {
			return reflect.ValueOf(nil), nil, fmt.Errorf("[VerifyFunc] %d-td argument`s type is incorrect", i+1)
		}
		vargs[i] = reflect.ValueOf(args[i])
	}
	return vf, vargs, nil
}

// CallFunc 动态调用函数.
func CallFunc(f interface{}, args ...interface{}) (results []interface{}, err error) {
	vf, vargs, _err := VerifyFunc(f, args...)
	if _err != nil {
		return nil, _err
	}
	ret := vf.Call(vargs)
	_len := len(ret)
	results = make([]interface{}, _len)
	for i := 0; i < _len; i++ {
		results[i] = ret[i].Interface()
	}
	return
}

// GetCallName 获取调用的方法名称;f为目标方法;onlyFun为true时仅返回方法,不包括包名.
func GetCallName(f interface{}, onlyFun bool) string {
	var fn *runtime.Func
	r, _ := reflectFinalValue(reflect.ValueOf(f))
	switch r.Kind() {
	case reflect.Invalid:
		// Skip this function, and fetch the PC and file for its parent
		pc, _, _, _ := runtime.Caller(1)
		// Retrieve a Function object this functions parent
		fn = runtime.FuncForPC(pc)
	case reflect.Func:
		fn = runtime.FuncForPC(r.Pointer())
	case reflect.Uintptr:
		fn = runtime.FuncForPC(f.(uintptr))
	default:
		return ""
	}

	name := fn.Name()
	if onlyFun {
		// extract just the function name (and not the module path)
		return strings.TrimPrefix(filepath.Ext(name), ".")
	}

	return name
}

// GetCallFile 获取调用方法的文件路径.
func GetCallFile() string {
	_, file, _, _ := runtime.Caller(1)
	return file
}

// GetCallDir 获取调用方法的文件目录.
func GetCallDir() string {
	return filepath.Dir(GetCallFile())
}

// GetCallLine 获取调用方法的行号.
func GetCallLine() int {
	// Skip this function, and fetch the PC and file for its parent
	_, _, line, _ := runtime.Caller(1)
	return line
}

// GetCallPackage 获取调用方法或调用文件的包名.callFile为调用文件路径.
func GetCallPackage(callFile ...string) string {
	var sourceFile string
	if len(callFile) == 0 {
		sourceFile = GetCallFile()
	} else {
		sourceFile = callFile[0]
	}

	fset := token.NewFileSet()
	astFile, err := parser.ParseFile(fset, sourceFile, nil, parser.PackageClauseOnly)
	if err != nil || astFile.Name == nil {
		return ""
	}

	return astFile.Name.Name
}

// HasMethod 检查对象t是否具有method方法.
func HasMethod(t interface{}, method string) bool {
	_, err := MethodExists(t, method)
	return err == nil
}

// CallMethod 调用对象的方法.
// 若执行成功,则结果是该方法的返回结果;
// 否则返回(nil, error).
func CallMethod(t interface{}, method string, args ...interface{}) ([]interface{}, error) {
	m := GetMethod(t, method)
	if m == nil {
		return nil, fmt.Errorf("[CallMethod] The %#v have no method: %s", t, method)
	}

	return CallFunc(m, args...)
}

// WrapError 错误包裹.
func WrapError(err error, args ...interface{}) (res error) {
	num := len(args)
	if err == nil && num == 0 {
		res = errors.New("[WrapError] parameter error")
	} else if err != nil && num == 0 {
		res = err
	} else {
		var msg []string
		for _, v := range args {
			msg = append(msg, ToString(v))
		}

		if err != nil {
			msg = append(msg, fmt.Sprintf("last error: %s", err.Error()))
		}

		res = errors.New(strings.Join(msg, "\r\n"))
	}

	return
}
